package org.dromara.demo.service;

import cn.hutool.core.util.StrUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.enums.UserStatus;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.excel.core.DropDownOptions;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.web.core.BaseService;
import org.dromara.demo.domain.vo.ExportDemoVo;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 导出下拉框Excel示例
 *
 * @author Emil.Zhang
 */
@Service
@RequiredArgsConstructor
public class ExportExcelServiceImpl extends BaseService {

  public void exportWithOptions(HttpServletResponse response) {
    // 创建表格数据，业务中一般通过数据库查询
    List<ExportDemoVo> excelDataList = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
      // 模拟数据库中的一条数据
      ExportDemoVo everyRowData = new ExportDemoVo();
      everyRowData.setNickName("用户-" + i);
      everyRowData.setUserStatus(UserStatus.OK.getCode());
      everyRowData.setGender("1");
      everyRowData.setPhoneNumber(String.format("175%08d", i));
      everyRowData.setEmail(String.format("175%08d", i) + "@163.com");
      everyRowData.setProvinceId(i);
      everyRowData.setCityId(i);
      everyRowData.setAreaId(i);
      excelDataList.add(everyRowData);
    }

    // 通过@ExcelIgnoreUnannotated配合@ExcelProperty合理显示需要的列
    // 并通过@DropDown注解指定下拉值，或者通过创建ExcelOptions来指定下拉框
    // 使用ExcelOptions时建议指定列index，防止出现下拉列解析不对齐

    // 首先从数据库中查询下拉框内的可选项
    // 这里模拟查询结果
    List<DemoCityData> provinceList = getProvinceList(),
      cityList = getCityList(provinceList),
      areaList = getAreaList(cityList);
    int provinceIndex = 5, cityIndex = 6, areaIndex = 7;

    DropDownOptions provinceToCity = DropDownOptions.buildLinkedOptions(
      provinceList,
      provinceIndex,
      cityList,
      cityIndex,
      DemoCityData::getId,
      DemoCityData::getPid,
      everyOptions -> DropDownOptions.createOptionValue(
        everyOptions.getName(),
        everyOptions.getId()
      )
    );

    DropDownOptions cityToArea = DropDownOptions.buildLinkedOptions(
      cityList,
      cityIndex,
      areaList,
      areaIndex,
      DemoCityData::getId,
      DemoCityData::getPid,
      everyOptions -> DropDownOptions.createOptionValue(
        everyOptions.getName(),
        everyOptions.getId()
      )
    );

    // 把所有的下拉框存储
    List<DropDownOptions> options = new ArrayList<>();
    options.add(provinceToCity);
    options.add(cityToArea);

    // 到此为止所有的下拉框可选项已全部配置完毕

    // 接下来需要将Excel中的展示数据转换为对应的下拉选
    List<ExportDemoVo> outList = StreamUtils.toList(excelDataList, everyRowData -> {
      // 只需要处理没有使用@ExcelDictFormat注解的下拉框
      // 一般来说，可以直接在数据库查询即查询出省市县信息，这里通过模拟操作赋值
      everyRowData.setProvince(buildOptions(provinceList, everyRowData.getProvinceId()));
      everyRowData.setCity(buildOptions(cityList, everyRowData.getCityId()));
      everyRowData.setArea(buildOptions(areaList, everyRowData.getAreaId()));
      return everyRowData;
    });

    ExcelUtil.exportExcel(outList, "下拉框示例", ExportDemoVo.class, response, options);
  }

  private String buildOptions(List<DemoCityData> cityDataList, Integer id) {
    Map<Integer, List<DemoCityData>> groupByIdMap =
      cityDataList.stream().collect(Collectors.groupingBy(DemoCityData::getId));
    if (groupByIdMap.containsKey(id)) {
      DemoCityData demoCityData = groupByIdMap.get(id).get(0);
      return DropDownOptions.createOptionValue(demoCityData.getName(), demoCityData.getId());
    } else {
      return StrUtil.EMPTY;
    }
  }

  /**
   * 模拟查询数据库操作
   *
   * @return /
   */
  private List<DemoCityData> getProvinceList() {
    List<DemoCityData> provinceList = new ArrayList<>();

    // 实际业务中一般采用数据库读取的形式，这里直接拼接创建
    provinceList.add(new DemoCityData(0, null, "安徽省"));
    provinceList.add(new DemoCityData(1, null, "江苏省"));

    return provinceList;
  }

  /**
   * 模拟查找数据库操作，需要连带查询出省的数据
   *
   * @param provinceList 模拟的父省数据
   * @return /
   */
  private List<DemoCityData> getCityList(List<DemoCityData> provinceList) {
    List<DemoCityData> cityList = new ArrayList<>();

    // 实际业务中一般采用数据库读取的形式，这里直接拼接创建
    cityList.add(new DemoCityData(0, 0, "合肥市"));
    cityList.add(new DemoCityData(1, 0, "芜湖市"));
    cityList.add(new DemoCityData(2, 1, "南京市"));
    cityList.add(new DemoCityData(3, 1, "无锡市"));
    cityList.add(new DemoCityData(4, 1, "徐州市"));

    selectParentData(provinceList, cityList);

    return cityList;
  }

  /**
   * 模拟查找数据库操作，需要连带查询出市的数据
   *
   * @param cityList 模拟的父市数据
   * @return /
   */
  private List<DemoCityData> getAreaList(List<DemoCityData> cityList) {
    List<DemoCityData> areaList = new ArrayList<>();

    // 实际业务中一般采用数据库读取的形式，这里直接拼接创建
    areaList.add(new DemoCityData(0, 0, "瑶海区"));
    areaList.add(new DemoCityData(1, 0, "庐江区"));
    areaList.add(new DemoCityData(2, 1, "南宁县"));
    areaList.add(new DemoCityData(3, 1, "镜湖区"));
    areaList.add(new DemoCityData(4, 2, "玄武区"));
    areaList.add(new DemoCityData(5, 2, "秦淮区"));
    areaList.add(new DemoCityData(6, 3, "宜兴市"));
    areaList.add(new DemoCityData(7, 3, "新吴区"));
    areaList.add(new DemoCityData(8, 4, "鼓楼区"));
    areaList.add(new DemoCityData(9, 4, "丰县"));

    selectParentData(cityList, areaList);

    return areaList;
  }

  /**
   * 模拟数据库的查询父数据操作
   *
   * @param parentList /
   * @param sonList    /
   */
  private void selectParentData(List<DemoCityData> parentList, List<DemoCityData> sonList) {
    Map<Integer, List<DemoCityData>> parentGroupByIdMap =
      parentList.stream().collect(Collectors.groupingBy(DemoCityData::getId));

    sonList.forEach(everySon -> {
      if (parentGroupByIdMap.containsKey(everySon.getPid())) {
        everySon.setPData(parentGroupByIdMap.get(everySon.getPid()).get(0));
      }
    });
  }

  /**
   * 模拟的数据库省市县
   */
  @Data
  private static class DemoCityData {
    /**
     * 数据库id字段
     */
    private Integer id;
    /**
     * 数据库pid字段
     */
    private Integer pid;
    /**
     * 数据库name字段
     */
    private String name;
    /**
     * MyBatisPlus连带查询父数据
     */
    private DemoCityData pData;

    public DemoCityData(Integer id, Integer pid, String name) {
      this.id = id;
      this.pid = pid;
      this.name = name;
    }
  }
}
